feat(ide): IntelliJ-grade Java in the web IDE — New→Java scaffolding, full JDT.LS IntelliSense, refactoring, Call/Type Hierarchy & performance#6076
Conversation
…ull Monaco Java IntelliSense Make writing client Java in the browser IDE feel like IntelliJ. New → Java submenu (Projects view): - Package (creates the nested folder chain from a dotted name) - Class, Interface, Enum, Annotation, Record, Exception - Controller, Job, Listener, WebSocket, Repository — strong-interface skeletons A fully-qualified name (com.test.MyClass) creates the package folders and the matching package declaration; a simple name lands in the selected folder. Logic lives in a new pure helper js/java-new.js; projects.js orchestrates dialog → nested folder → file → open via the existing WorkspaceService. Java IntelliSense (editor-monaco LSP client): - Auto-import on completion (completionItem/resolve → additionalTextEdits) - Dirigible SDK suggestions ranked first (sortText bucketing) - Code actions: quick-fixes, organize imports, and generate constructor/getters/setters/toString/equals&hashCode with a member-picker dialog - Rename symbol, find references, document formatting - Server→client workspace/applyEdit + configuration handling - Format-on-save for Java reuses the existing TypeScript auto-format mechanism via the new formatting provider (same global toggle) Tests: - CreateJavaArtifactsIT drives New → Java end to end and asserts folders, files, package declarations and skeleton signals over the workspace REST API - Browser.hoverOnElementByAttributePatternAndText + Workbench.createJavaArtifact Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…avaArtifactsIT pass
JDT.LS was launched with -Xmx512m, which OOMs ("Java heap space") while indexing
the full platform classpath (~1000+ entries), killing the language server and with
it completion / auto-import / code actions. Make the max heap configurable via
DIRIGIBLE_JAVA_LSP_MAX_HEAP (DirigibleConfig.JAVA_LSP_MAX_HEAP) and default it to 2g.
Test framework: CreateJavaArtifactsIT now drives the nested New → Java → <item>
context menu reliably. Replaced the strict hover/visibility approach (which cannot
reach a collapsed 3rd-level submenu in headless Chrome, where moveToElement does not
fire mouseenter) with Browser.clickCascadingMenuItem, which scopes to the open
context menu and drives its own Angular mouseenter/click handlers in a single
in-frame script. Verified: the IT passes end to end (package folders, package
declarations and strong-interface skeleton signals all asserted over the workspace
REST API).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow-up while testing locallyJDT.LS heap fix. JDT.LS launched with Integration test now green. |
…ntity is an input - After creating a Java artefact the project tree now expands to and selects the new file (building/reusing the package folder nodes) instead of reloading the project node, which collapsed the structure. - The Repository skeleton no longer reuses the repository class name as its entity type parameter (DemoRepo → JavaRepository<DemoRepo>). The dialog now prompts for the entity type (simple or fully-qualified; a qualified name adds the import), producing e.g. JavaRepository<Country> with super(Country.class). - CreateJavaArtifactsIT + Workbench.createJavaArtifact updated for the entity prompt. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…mpletion - Completion now propagates JDT.LS's isIncomplete so Monaco re-queries as you type (fixes the first Ctrl+Space showing nothing), and forwards the real trigger kind/character plus filterText/preselect/commitCharacters for correct filtering. - Registered a Monaco editor opener: Go to Definition / Find References to a symbol in another workspace file now opens that file in the IDE and reveals the line (the single-file editor previously had no model for other files, so navigation silently did nothing). Same-file targets still jump within the editor. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… file Renaming a Java symbol (F2) now applies JDT.LS's full rename WorkspaceEdit across the whole workspace instead of only the current file: - Text edits in every referencing file are applied and persisted (read-modify-write over the workspace REST API, CSRF-guarded); the current file goes through the live Monaco model + save. - RenameFile operations are honoured, so renaming a public class/interface/enum also renames its .java file and updates all references; when that file is the one being edited, the editor tab switches to the new file. - Other open editors reload the changed files (dirty editors are skipped to avoid clobbering unsaved work) via a new monaco.file.reload topic. Falls back to current-file-only rename if the IDE persistence hook is unavailable. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s, semantic colors, CodeLens, override/implement, keywords Adds JDT.LS-backed language features to the Monaco Java editor: Navigation & structure: - Outline / breadcrumbs / sticky scroll (documentSymbol), code folding (foldingRange), occurrence highlight (documentHighlight), Go to Implementation and Go to Type Definition (reusing the cross-file editor opener), Format Selection (rangeFormatting) and Smart Expand Selection (selectionRange). Editor intelligence: - Inlay hints (parameter names + inferred types) and semantic-token highlighting (legend captured from the initialize result; semanticHighlighting enabled on the editor). - CodeLens with "N references / N implementations" (click opens the peek). - Override/Implement Methods wired into the existing member-picker generate flow. - Always-available Java keyword completion, ranked below SDK/LSP results. Advertises the matching client capabilities and enables the JDT.LS inlay-hint and references/implementations CodeLens settings. All are additive providers; the exact JDT.LS command ids are guarded. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…yword colors - Find/Peek References (and go-to-definition/implementation/type-definition) now create in-memory models for the referenced files, so the peek shows a real code preview instead of only file/line/column. - Flush the pending debounced didChange before a completion request, so JDT.LS sees the just-typed text on the first Ctrl+Space (previously the first invocation completed against stale content and only the second worked). - Revert the forced semanticHighlighting flag: JDT.LS emits keyword/modifier semantic tokens the vs-dark-based themes don't style, which left Java keywords uncolored. The semantic-tokens provider stays registered (inert until a theme opts in) so basic Monarch keyword coloring is restored. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e after rename Root cause of "no quick-fixes / no Implement Methods / Generate toString does nothing": - We advertised the *PromptSupport extended capabilities, so JDT.LS returned source actions (generate toString/constructors/accessors, override/implement, organize imports) as client-side "java.action.*Prompt" commands that the vscode-java extension implements but we don't — and the server commands we tried to call (checkToStringStatus/generateToString/…) don't exist on JDT.LS. Dropping those flags makes JDT.LS return the same actions as resolvable WorkspaceEdits (all members), which applyCodeAction resolves and applies directly. - Code-action requests sent diagnostics reconstructed from Monaco markers, which lose the LSP code/data JDT.LS matches quick-fixes against. We now keep the original published diagnostics per file and send the ones overlapping the request range, so "Add unimplemented methods", "Create field", etc. appear. Also: the Projects view ignored file rename/move events, so after a class rename the tree still showed the old file. It now reloads on platform.files.renamed/moved. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n completion) Renaming a public type (F2) had two defects: - When JDT.LS ordered the RenameFile before the text edit, the edit was keyed by the new URI; we wrote the old content (old type name) into the new file, producing "The public type X must be defined in its own file". Edits are now re-attributed to the on-disk (old) URI before applying, so the new file gets the new type name. - After renaming on disk we never told JDT.LS, so it kept validating stale state until a page refresh and kept suggesting the old type name in completion. We now send textDocument/didClose for the renamed-away file and workspace/didChangeWatchedFiles (Deleted old / Created new / Changed edited) so the server re-indexes immediately — clearing the stale diagnostic and dropping the old name from completion. This also removes the exception/fallback path that made the first Enter occasionally apply only the in-editor edit without renaming the file. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Cap JDT.LS completion at maxResults=50 so import/type suggestions over the large platform classpath return faster. - On a Java file rename, the Projects view now renames the matching tree node in place and selects it (keeping it revealed) instead of doing a full collapse-reload, so the renamed file stays visible and highlighted; falls back to a reload if the node isn't loaded. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…meter to field) Set the Monaco code-action lightbulb to "on" so it appears whenever any code action is available at the cursor — not only on diagnostics. This surfaces JDT.LS refactor assists like "Assign parameter to new field" / "Assign all parameters to new fields" (kind refactor.assign.field) on a constructor parameter, plus Extract Method/Variable/ Constant/Field, as a visible suggestion instead of being hidden behind Refactor…. The assist itself already flowed through the existing code-action provider. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ce CodeLens The Java editor had become slow to "enable" features on a fresh project and sluggish while typing. Two avoidable load sources, on top of JDT.LS's inherent cold-start indexing of the large platform classpath: - Code-action requests omitted triggerKind, so JDT.LS computed the full set of quick assists/refactorings on every passive-lightbulb evaluation (every cursor move). Now we forward Monaco's trigger (Invoke=1/Auto=2, 1:1 with the LSP kind): the passive lightbulb computes only quick-fixes (cheap); full assists/refactorings only on explicit Ctrl+. / Refactor…. - references/implementations CodeLens ran a reference search for every declaration on open and on every edit — the heaviest always-on provider. Disabled by default. - Reverted the eager `lightbulb: 'on'` (its purpose was to surface refactor assists in the passive bulb, which we instead keep on the explicit Refactor… surface). Refactor assists like "Assign parameter to new field" / Extract remain available via Refactor… (Ctrl+Shift+R). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ling / CreateNewFileIT) BlimpKit's context menu disables scrolling for any submenu that contains a nested submenu (contextmenu.js canScroll). Nesting the "Java" group inside "New" therefore turned off scrolling for the whole New menu, so once enough artefact templates are registered the menu overflows the viewport and top items (e.g. "JavaScript Service") become unreachable — which failed CreateNewFileIT. Move "Java" to a top-level context-menu entry (sibling of New) on project/folder nodes. New keeps its scrolling; "Java" is its own 2-level submenu. Workbench page object navigates Java → <item> accordingly. Verified: CreateNewFileIT and CreateJavaArtifactsIT both pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ath; index freshness The Java editor was slow to "enable" features after every jar restart because JDT.LS re-indexed the whole platform classpath each time. Causes and fixes: - JdtLsManager wiped the JDT.LS data/index dir on every start. Since ClassPathIndex now extracts to a stable cache dir, the index only goes stale when the classpath actually changes. Wipe is now guarded by a classpath fingerprint (SHA-256 of the sorted classpath entries) stored next to the data dir: matching fingerprint → reuse the index (warm, fast restart); mismatch/first run → rebuild once. - Pre-warm the (cached) compile classpath on the boot virtual thread so the first Java file open doesn't pay the multi-second materialisation. - After didOpen, send workspace/didChangeWatchedFiles(Created) so JDT.LS's project model includes just-created files immediately (a fresh interface is now offered in a sibling class without waiting for a rebuild). - Dropped java.completion.maxResults (it equalled JDT.LS's default and only risked trimming relevant results such as the project's own types). Verified: CreateJavaArtifactsIT + CreateNewFileIT pass; logs show "Pre-warmed compile classpath" and the fingerprint-guarded "Building/Reusing JDT.LS index". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…on save, Surround With - Enable JDT.LS postfix completion (expr.var/.for/.fori/.sout/.null/.notnull/.cast/...). - Organize imports on save for Java: the Save action now runs organize-imports → format → save (Java only; failures still fall through to save). TS/other files keep format-then-save. - Add a "Java: Surround With / Refactor..." editor action (Cmd/Ctrl+Alt+T) that opens the refactor picker (surround try-catch/if, extract, ...). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Enable `semanticHighlighting.enabled` and give every theme an explicit `semanticHighlighting: true` flag plus colour rules for the full JDT.LS legend (namespace/class/interface/enum/type/typeParameter/annotation/enumMember/ method/field/property/variable/parameter) AND keyword/modifier — mapping the modifier-bearing keyword tokens is what stops keywords blanking out under the semantic layer (the earlier regression). Adds a custom `blimpkit-light` theme (base vs) so light mode gets the same explicit rules instead of bare vs-light; dark uses blimpkit-dark/classic-dark. Palettes mirror VS Code dark+/light+. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…(Cmd/Ctrl+T)
Views outside the Monaco iframe can't reuse the editor's per-iframe LSP socket, so
add an HTTP facade that drives the shared JDT.LS instance directly:
- JavaLspQueryEndpoint (/services/ide/java-lsp): GET /symbol plus
call-hierarchy/{prepare,incoming,outgoing} and type-hierarchy/{prepare,supertypes,
subtypes}. Each getOrStart → ensureInitialized → sendRequest, returning the LSP
result; URIs travel as virtual file:///workspace/<ws>/... (bridge-translated).
- Advertise workspace/symbol + textDocument/{callHierarchy,typeHierarchy} in BOTH
the server-side initialize and the editor client initialize, so the capabilities
survive whichever side last (re)initializes the shared JDT.LS process.
- New view-java-symbols (left region): debounced workspace symbol search, kind
icons, container + path subtitle, click-to-open at the symbol's line. Registered
in components/pom.xml, group-ide, and the workbench views list.
- Editor action "Java: Go to Symbol in Workspace..." (Cmd/Ctrl+T) focuses the view
and seeds it with the current selection / word under cursor.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New bottom-region view-java-hierarchy driven by the JavaLspQueryEndpoint facade:
- Editor actions "Show Call Hierarchy" (Ctrl+Alt+H) and "Show Type Hierarchy"
(Ctrl+H) post java.hierarchy.show {kind, workspace, uri, line, character} for the
symbol under the cursor and focus the panel.
- The panel resolves the root via prepare, then lazily expands each node:
call -> incoming/outgoing, type -> supertypes/subtypes, with a toolbar toggle
(Callers/Callees, Supertypes/Subtypes). Custom flat-tree model (depth-indented
rows, lazy fetch on expand) — no jstree dependency. Click navigates to the node's
declaration. Registered in components/pom.xml, group-ide, and the workbench views.
- Fix view-java-symbols path mapping: strip only the file:///workspace prefix so the
workspace segment is preserved (/<ws>/<proj>/File.java), matching the editor's
resourcePath; the previous version dropped the workspace and opened a wrong path.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… race Go to Symbol in Workspace is redundant with Go to References / Implementations (and its view's AngularJS failed to bootstrap), so remove it entirely: - delete view-java-symbols and its registrations (components/pom.xml, group-ide, workbench views list); - drop the editor "Go to Symbol in Workspace" action; - drop the now-dead GET /symbol endpoint and the workspace/symbol capability from both the server-side and editor-client initialize. Call/Type Hierarchy did nothing on right-click: the editor posted java.hierarchy.show immediately after openView, before the lazily-loaded panel registered its listener, so the request was missed. Re-post the request (0/400/1200ms, same pattern as javaLspOpenFile's reveal re-post) and dedupe identical requests in the panel within a 3s window so the retries don't refetch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…Boot 4's Jackson 3 converter Every call/type hierarchy POST 500'd before reaching the handler: HttpMessageConversionException "Cannot construct instance of com.fasterxml.jackson.databind.JsonNode". Spring Boot 4's default HTTP message converter is Jackson 3 (tools.jackson.databind) and cannot bind a request body to a Jackson 2 (com.fasterxml.jackson) JsonNode, which is what JdtLsInstance uses. Keep the controller boundary off Jackson-2 types: accept the body as a raw String, parse it with this module's own Jackson 2 ObjectMapper, and return raw JSON as a String with application/json. No JsonNode parameter or return value is exposed to Spring's converter anymore. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 96 javascript quality alerts (useless-assignment-to-local, trivial-conditional, unused-local-variable) all originate in java-lsp-client.js, which is the esbuild output of java-lsp-client.ts and embeds vendored vscode-languageserver protocol/types. The hand-written .ts source is still analysed; only the generated bundle is ignored. The 4 java/log-injection alerts in JdtLsManager are dismissed as false positives: the logged username/workspace already pass through sanitize() which strips [\r\n\t] (the CWE-117 vector); CodeQL just doesn't recognise the custom sanitizer helper. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… executeJavaScript clickCascadingMenuItem navigated the menu via one injected DOM script, which stuck out against the rest of BrowserImpl (built on Selenide element APIs). Rewrite it using Selenide: locate the open context menu once (findElementInAllFrames leaves the driver in the menu's iframe), then hover each intermediate submenu title to expand its flyout — BlimpKit expands on mouseenter and keeps it open while the pointer moves into the flyout — and click the leaf. Staying within the menu's frame via the returned element handle is the crux: re-searching per step restarts at defaultContent() and would close the menu (the original reason a single script was used). Removes CASCADE_MENU_SCRIPT (the only Selenide.executeJavaScript usage in the class). Verified: CreateJavaArtifactsIT passes headless. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Turns the in-browser Monaco editor into an IntelliJ-grade Java environment: scaffolding new types,
full JDT.LS-backed IntelliSense, refactoring and code generation, Call/Type Hierarchy, and the
warm-up performance work to make it all feel instant.
All Java intelligence is served by a per-workspace JDT Language Server (
ide-java-lsp), bridgedto the editor over WebSocket and — for views that live outside the editor iframe — over a small
server-side HTTP facade.
New → Java scaffolding (Projects view)
A dedicated Java context-menu (project & folder) creates, with automatic package-folder creation
and a matching
packagedeclaration:org.eclipse.dirigible.sdk.*style: Controller, Job, Listener,WebSocket, Repository (Repository also prompts for the entity type it manages).
A fully-qualified name creates the folder chain; a simple name lands in the project root. The created
file is opened and revealed/selected in the tree.
Editor IntelliSense (Monaco ⇄ JDT.LS)
Completion with auto-import and SDK-prioritised ordering, first-
Ctrl+Spaceresponsiveness,hover, signature help, document highlight, outline / document symbols, folding, selection
ranges, inlay parameter-name hints, and Java keyword suggestions.
Navigation works across files (the editor holds a single-file model, so cross-file targets are
fetched and opened on demand): Go to Definition, Find References (with code preview), Go to
Implementations, Go to Type Definition.
Refactoring & code generation
renamed, renames the type's own
.javafile; the Projects tree updates in place and the renamedfile is revealed. Stale-error / old-name-in-completion sync issues fixed.
toString/ getters & setters,override/implement methods, implement required interface methods, organize imports.
constructor parameter and extract refactorings.
Formatting, imports & completion polish
expr.var,.for,.fori,.sout,.null, …).Semantic highlighting
Real JDT.LS semantic tokens with explicit colour rules for the full legend in
blimpkit-dark,classic-dark, and a newblimpkit-lighttheme. Keyword/modifier tokens are mapped explicitly sokeywords don't blank out under the semantic layer (palettes mirror VS Code dark+/light+).
Call & Type Hierarchy (new bottom panel)
view-java-hierarchyplus editor actions Show Call Hierarchy (Ctrl+Alt+H) and Show TypeHierarchy (Ctrl+H). The panel resolves the root and lazily expands each node — call → Callers /
Callees, type → Supertypes / Subtypes (toolbar toggle); clicking a node navigates to its declaration.
Backed by a new server facade
JavaLspQueryEndpoint(/services/ide/java-lsp) that drives theshared JDT.LS instance for these non-editor views. The corresponding
callHierarchy/typeHierarchycapabilities are advertised in both the server-side and editor-client
initialize, so theysurvive whichever side last (re)initializes the shared process. (URIs travel as the browser's virtual
file:///workspace/<ws>/…form and are bridge-translated to/from real paths.)Performance
every boot; rebuilt only when the platform classpath actually changes.
DIRIGIBLE_JAVA_LSP_MAX_HEAP(default2g).triggerKindplumbed through; references/implementations CodeLenskept off by default.
Tests & CI
CreateJavaArtifactsITdrives the New → Java menu and asserts each artefact's location and contentover the workspace REST API; test framework gains a cascading-menu click helper.
java-lsp-client.jsbundle (esbuild output embedding vendoredvscode-languageservertypes — the.tssource is still analysed); 4java/log-injectionalertsdismissed as false positives (the logged username/workspace already pass through
sanitize()whichstrips
\r\n\t).Notes
project.
to pick up editor/view changes.
🤖 Generated with Claude Code